package com.chuangke.admin.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.chuangke.admin.constant.SysConstant;
import com.chuangke.admin.dao.SysDeptDaoService;
import com.chuangke.admin.entity.SysDept;
import com.chuangke.admin.service.SysDeptService;
import com.chuangke.common.constant.CacheKeyConstants;
import com.chuangke.common.constant.ColumnConstants;
import com.chuangke.common.constant.FileConstants;
import com.chuangke.common.db.page.CkPageQuery;
import com.chuangke.common.event.file.FileDbankRegistEvent;
import com.chuangke.common.exception.ChuangkeException;
import com.chuangke.framework.model.Record;
import com.chuangke.framework.util.TreeUtils;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SysDeptServiceImpl implements SysDeptService {

    public static final String ONE_LEVEL_DEPT = "1";
    public static final String TWO_LEVEL_DEPT = "2";
    public static final String THREE_LEVEL_DEPT = "3";

    @Autowired
    private SysDeptDaoService sysDeptDaoService;
    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @Override
    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_DEPT, key = "'" + CacheKeyConstants.SYS_DEPT + "'")
    public void save(SysDept record) {
        ((SysDeptService) AopContext.currentProxy()).doSave(record);
        level();
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void save(List<SysDept> records) {
        throw new ChuangkeException("暂不支持的方法");
    }

    /**
     * 对数据进行分级，更新数据的level,is_last_level,data_domain
     */
    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_DEPT, key = "'" + CacheKeyConstants.SYS_DEPT + "'")
    public void level() {
        List<SysDept> list = findTree();
        List<SysDept> newList = new ArrayList<>();
        TreeUtils.level("", 0, list, newList);
        doUpdateBatch(newList);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_DEPT, key = "'" + CacheKeyConstants.SYS_DEPT + "'")
    public void doSave(SysDept record) {
        List<SysDept> filterList = sysDeptDaoService.list();
        filterList = filterList.stream().filter(item -> Objects.equals(item.getId(), record.getId()))
                .collect(Collectors.toList());
        if (filterList.isEmpty()) {
            String deptId = record.getId();
            sysDeptDaoService.save(record);
            eventPublisher.publishEvent(new FileDbankRegistEvent(this, deptId, "e", record.getName() + "的网盘",
                    new BigInteger(FileConstants.DEFAULT_DBANK_SIZE)));
        } else {
            sysDeptDaoService.update(record, new UpdateWrapper<SysDept>().lambda().eq(SysDept::getId, record.getId()));
        }
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_DEPT, key = "'" + CacheKeyConstants.SYS_DEPT + "'")
    public void doUpdateBatch(List<SysDept> list) {
        sysDeptDaoService.updateBatchById(list);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_DEPT, key = "'" + CacheKeyConstants.SYS_DEPT + "'")
    @Override
    public void delete(SysDept record) {
        List<SysDept> children = findAllChildren(Collections.singletonList(record.getId()));
        List<SysDept> allRecords = TreeUtils.treeToList(children);
        if (CollectionUtils.isEmpty(allRecords)) {
            return;
        }
        sysDeptDaoService.removeBatchByIds(allRecords);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_DEPT, key = "'" + CacheKeyConstants.SYS_DEPT + "'")
    @Override
    public void delete(List<SysDept> records) {
        List<String> idList = records.stream().map(SysDept::getId).collect(Collectors.toList());
        List<SysDept> children = findAllChildren(idList);
        List<SysDept> allRecords = TreeUtils.treeToList(children);
        if (CollectionUtils.isEmpty(allRecords)) {
            return;
        }
        sysDeptDaoService.removeBatchByIds(allRecords);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_DEPT, key = "'" + CacheKeyConstants.SYS_DEPT + "'")
    @Override
    public void deleteByIds(List<String> ids) {
        sysDeptDaoService.removeByIds(ids);
    }

    @Cacheable(cacheNames = CacheKeyConstants.SYS_DEPT, key = "'" + CacheKeyConstants.SYS_DEPT + "'")
    @Override
    public Map<String, SysDept> findMap() {
        List<SysDept> list = sysDeptDaoService.list();
        return list.stream().collect(Collectors.toMap(SysDept::getId, a -> a));
    }

    @Override
    public List<SysDept> findList() {
        Map<String, SysDept> map = ((SysDeptService) AopContext.currentProxy()).findMap();
        return new ArrayList<>(map.values());
    }

    @Override
    public List<SysDept> findTree() {
        List<SysDept> depts = ((SysDeptService) AopContext.currentProxy()).findList();
        return TreeUtils.buildTree(depts);
    }

    @Override
    public List<SysDept> findTree(String condition) {
        if (StrUtil.isBlank(condition)) {
            return findTree();
        }

        List<SysDept> depts = ((SysDeptService) AopContext.currentProxy()).findList();
        List<String> ids = depts.stream().filter(dept -> dept.getId().contains(condition) || dept.getName().contains(condition)).map(SysDept::getId).collect(Collectors.toList());
        return findKinshipTree(ids);
    }

    @Override
    public List<SysDept> findByDeptType(String deptType) {
        List<SysDept> depts = ((SysDeptService) AopContext.currentProxy()).findList();
        return depts.stream().filter(dept -> deptType.equals(dept.getDeptType())).collect(Collectors.toList());
    }

    @Override
    public List<SysDept> findListWithChildren() {
        List<SysDept> list = findTree();
        return TreeUtils.treeToList(list);
    }

    @Override
    public SysDept findById(String id) {
        if (id == null) {
            return null;
        }

        Map<String, SysDept> map = ((SysDeptService) AopContext.currentProxy()).findMap();
        return map.get(id);
    }

    @Override
    public List<SysDept> findLastLevel() {
        List<SysDept> treeList = findTree();

        List<SysDept> lastLevelDeptList = new ArrayList<>();
        TreeUtils.findLastLevel(treeList, lastLevelDeptList);

        return lastLevelDeptList;
    }

    @Override
    public List<SysDept> findByLevel(int level) {
        List<SysDept> list = ((SysDeptService) AopContext.currentProxy()).findList();
        return TreeUtils.findByLevel(list, level);
    }

    @Override
    public List<SysDept> findLastLevel(String deptId) {
        List<SysDept> list = findListWithChildren();
        Map<String, SysDept> map = list.stream().collect(Collectors.toMap(SysDept::getId, a -> a));

        List<SysDept> deptTreeList = new ArrayList<>();
        deptTreeList.add(map.get(deptId));

        List<SysDept> lastLevelDeptList = new ArrayList<>();
        TreeUtils.findLastLevel(deptTreeList, lastLevelDeptList);
        return lastLevelDeptList;
    }

    @Override
    public List<SysDept> findNextLevel(String deptId) {
        List<SysDept> list = findListWithChildren();
        Map<String, SysDept> map = list.stream().collect(Collectors.toMap(SysDept::getId, a -> a));
        return map.get(deptId) == null ? null : map.get(deptId).getChildren();
    }

    @Override
    public List<SysDept> findKinshipTree(Collection<String> deptIdList) {
        Map<String, SysDept> noChildMap = ((SysDeptService) AopContext.currentProxy()).findMap();
        List<SysDept> withChildrenList = findListWithChildren();
        return TreeUtils.findKinshipTree(noChildMap, withChildrenList, deptIdList);
    }

    @Override
    public SysDept findParent(String deptId) {
        Map<String, SysDept> noChildMap = ((SysDeptService) AopContext.currentProxy()).findMap();
        return TreeUtils.getParent(deptId, noChildMap);
    }

    @Override
    public List<SysDept> findAllChildren(List<String> idList) {
        List<SysDept> list = findListWithChildren();
        list = list.stream().filter(dept -> idList.contains(dept.getId())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        List<SysDept> result = new ArrayList<>();
        TreeUtils.foreachChildren(result, list);

        return result;
    }

    @Override
    public IPage<SysDept> page(CkPageQuery page) {
        throw new ChuangkeException("暂不支持的方法");
    }

    @Override
    public List<String> findDeptByRule(String deptId, String rule) {
        if (rule == null || rule.trim().isEmpty()) {
            throw new ChuangkeException("请先设置角色权限！");
        }
        List<SysDept> list = new ArrayList<>(((SysDeptService) AopContext.currentProxy()).findMap().values());
        list = list.stream().filter(item -> deptId != null && deptId.equals(item.getId())).collect(Collectors.toList());
        List<String> deptIds = new ArrayList<>();
        switch (rule) {
            case SysConstant.RULE_CUSTOM_DEPT:
                break;
            case SysConstant.RULE_All:
                deptIds = ((SysDeptService) AopContext.currentProxy()).findMap().values().stream().map(SysDept::getId)
                        .collect(Collectors.toList());
                break;
            case SysConstant.RULE_LOCAL_DEPT:
                deptIds.add(deptId);
                break;
            case SysConstant.RULE_LOCAL_SON_DEPT:
                if (!list.isEmpty()) {
                    SysDept sysDept = list.get(0);
                    List<SysDept> localSonList = sysDeptDaoService.list(
                            new QueryWrapper<SysDept>().lambda().like(SysDept::getDataDomain, sysDept.getDataDomain()));
                    deptIds.addAll(localSonList.stream().map(SysDept::getId).collect(Collectors.toList()));
                }
                break;
            case SysConstant.RULE_LOCAL_ONE_LEVEL:
                if (!list.isEmpty()) {
                    SysDept sysDept = list.get(0);
                    String dataDomain = sysDept.getDataDomain();
                    if (dataDomain != null && dataDomain.length() > 4) {
                        dataDomain = dataDomain.substring(0, 4);
                    }
                    List<SysDept> localSonList = sysDeptDaoService.list(new QueryWrapper<SysDept>().lambda()
                            .like(SysDept::getDataDomain, dataDomain).eq(SysDept::getLevel, ONE_LEVEL_DEPT));
                    deptIds.addAll(localSonList.stream().map(SysDept::getId).collect(Collectors.toList()));
                }
                break;
            case SysConstant.RULE_LOCAL_TWO_LEVEL:
                if (!list.isEmpty()) {
                    SysDept sysDept = list.get(0);
                    String dataDomain = sysDept.getDataDomain();
                    if (dataDomain != null && dataDomain.length() > 8) {
                        dataDomain = dataDomain.substring(0, 8);
                    }
                    List<SysDept> localSonList = sysDeptDaoService.list(new QueryWrapper<SysDept>().lambda()
                            .like(SysDept::getDataDomain, dataDomain).eq(SysDept::getLevel, TWO_LEVEL_DEPT));
                    deptIds.addAll(localSonList.stream().map(SysDept::getId).collect(Collectors.toList()));
                }
                break;
            case SysConstant.RULE_LOCAL_THREE_LEVEL:
                if (!list.isEmpty()) {
                    SysDept sysDept = list.get(0);
                    String dataDomain = sysDept.getDataDomain();
                    if (dataDomain != null && dataDomain.length() > 12) {
                        dataDomain = dataDomain.substring(0, 12);
                    }
                    List<SysDept> localSonList = sysDeptDaoService.list(new QueryWrapper<SysDept>().lambda()
                            .like(SysDept::getDataDomain, dataDomain).eq(SysDept::getLevel, THREE_LEVEL_DEPT));
                    deptIds.addAll(localSonList.stream().map(SysDept::getId).collect(Collectors.toList()));
                }
                break;
            default:
                break;
        }
        return deptIds;
    }

    @Override
    public SysDept findParentByDeptType(String deptId, String deptType) {
        SysDept sysDept = ((SysDeptService) AopContext.currentProxy()).findById(deptId);
        if (sysDept == null || sysDept.getDeptType() == null) {
            return null;
        }
        
        if (sysDept.getDeptType().equals(deptType)) {
            return sysDept;
        }

        SysDept parent = ((SysDeptService) AopContext.currentProxy()).findParent(deptId) ;
        if(parent==null || parent.getId().equals(sysDept.getId())){
            return null;
        }

        if(parent.getDeptType().equals(deptType)){
            return parent;
        }else{
            return ((SysDeptService) AopContext.currentProxy()).findParentByDeptType(parent.getId(), deptType);
        }
    }

    @Override
    public Map<String, Record> getStandardData() {
        Map<String, SysDept> map = ((SysDeptService) AopContext.currentProxy()).findMap();
        List<Record> list = new ArrayList<>(map.size());
        for (SysDept sd : map.values()) {
            Record record = new Record();
            record.put(ColumnConstants.COL_NAME_CODE, sd.getId());
            record.put(ColumnConstants.COL_NAME_NAME, sd.getName());
            record.put(ColumnConstants.COL_NAME_LEVEL, sd.getLevel());
            record.put(ColumnConstants.COL_NAME_IS_LAST_LEVEL, sd.getIsLastLevel());
            record.put(ColumnConstants.COL_NAME_DATA_DOMAIN, sd.getDataDomain());
            record.put(ColumnConstants.COL_NAME_ID, sd.getId());
            record.put(ColumnConstants.COL_NAME_PARENT_ID, sd.getParentId());
            list.add(record);
        }
        return list.stream().collect(Collectors.toMap(record -> record.getStr(ColumnConstants.COL_NAME_CODE), a -> a));
    }

    @Override
    public List<SysDept> clone(List<SysDept> deptList) {
        List<SysDept> newDeptList = new ArrayList<>();
        for (SysDept dept : deptList) {
            SysDept newAccount;
            try {
                newAccount = (SysDept) BeanUtils.cloneBean(dept);
                newDeptList.add(newAccount);
                if (!CollectionUtils.isEmpty(dept.getChildren())) {
                    newAccount.setChildren(clone(dept.getChildren()));
                }
            } catch (Exception e) {
                throw new ChuangkeException(e);
            }
        }
        return newDeptList;
    }

	@Override
	public SysDept findByName(String name) {
		if (StrUtil.isBlank(name)) {
            return null;
        }

        List<SysDept> list = ((SysDeptService) AopContext.currentProxy()).findList() ;
        for(SysDept dept : list) {
        	if(dept.getName().equals(name)) {
        		return dept ;
        	}
        }
        return null ;
	}

}
